package com.ikas.ai.server.module.morningpaper.service;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.ikas.ai.consts.Consts;
import com.ikas.ai.consts.MachineAndWorkConditionConst;
import com.ikas.ai.consts.TableDictConsts;
import com.ikas.ai.enums.redis.RedisKeyEnum;
import com.ikas.ai.server.error.BusinessException;
import com.ikas.ai.server.module.cockpit.model.dto.StartCountStatisticsDTO;
import com.ikas.ai.server.module.condition.dao.WorkFlowInstanceResultDao;
import com.ikas.ai.server.module.condition.model.WorkFlowInstanceResult;
import com.ikas.ai.server.module.data.model.BaseMete;
import com.ikas.ai.server.module.data.model.tdengine.RealDataHis;
import com.ikas.ai.server.module.data.tdDao.DataHisTdMapper;
import com.ikas.ai.server.module.morningpaper.dao.MorningPaperConstsDao;
import com.ikas.ai.server.module.morningpaper.dao.RuntimeDataStatisticsDao;
import com.ikas.ai.server.module.morningpaper.model.dto.FailureRecord;
import com.ikas.ai.server.module.morningpaper.model.dto.RuntimeDataStatisticsDetailDTO;
import com.ikas.ai.server.module.morningpaper.model.dto.RuntimeDataStatisticsEditDTO;
import com.ikas.ai.server.module.morningpaper.model.enums.ReviewedEnum;
import com.ikas.ai.server.module.morningpaper.model.po.MorningPaperConsts;
import com.ikas.ai.server.module.morningpaper.model.po.RuntimeDataStatistics;
import com.ikas.ai.server.module.system.model.User;
import com.ikas.ai.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author li.hexian
 * 执行失败记录相关接口及工具
 */
@Service
@Slf4j
public class RuntimeDataStatisticsService extends ServiceImpl<RuntimeDataStatisticsDao, RuntimeDataStatistics> {

    @Autowired
    private FailureRecordService failureRecordService;
    @Autowired
    private DataHisTdMapper dataHisTdMapper;
    @Autowired
    protected RedisUtil redisUtil;
    @Autowired
    private RuntimeDataStatisticsDao runtimeDataStatisticsDao;
    @Autowired
    private WorkFlowInstanceResultDao workFlowInstanceResultDao;
    @Autowired
    private MorningPaperConstsDao baseMeteConstsDao;
    @Value("${td.offset.second:10}")
    private Integer second;

    private static final String TITLE = "【运行数据上报】%s";
    private static final String UP_CAPACITY = "UPCAPACITY";
    private static final String DOWN_CAPACITY = "DOWNCAPACITY";
    private static final String QCZYG = "QCZYG";
    private static final String CRK00_MODE_DZ_P = "CRK00_MODE_DZ_P";
    private static final String CRK00_MODE_DZ_G = "CRK00_MODE_DZ_G";

    private static final BigDecimal DIVIDE_FACTOR = new BigDecimal(1000 * 60 * 60);

    public void edit(RuntimeDataStatisticsEditDTO runtimeDataStatisticsEditDTO, User user) {
        RuntimeDataStatistics runtimeDataStatistics = runtimeDataStatisticsDao.selectById(runtimeDataStatisticsEditDTO.getId());
        if (Objects.isNull(runtimeDataStatistics)) {
            runtimeDataStatistics = new RuntimeDataStatistics();
        }
        if (runtimeDataStatistics.getActualPowerGeneration() != null && !runtimeDataStatistics.getActualPowerGeneration().equals(runtimeDataStatisticsEditDTO.getActualPowerGeneration())) {
            //不允许修改实际发电量
            if (!Double.valueOf(0.0).equals(runtimeDataStatistics.getActualPowerGeneration())) {
                throw new BusinessException("实际发电量不允许再次编辑");
            }
        }
        if (runtimeDataStatistics.getActualWaterPumpingPower() != null && !runtimeDataStatistics.getActualWaterPumpingPower().equals(runtimeDataStatisticsEditDTO.getActualWaterPumpingPower())) {
            //不允许修改实际抽水电量
            if (!Double.valueOf(0.0).equals(runtimeDataStatistics.getActualWaterPumpingPower())) {
                throw new BusinessException("实际抽水电量不允许再次编辑");
            }
        }
        BeanUtils.copyProperties(runtimeDataStatisticsEditDTO, runtimeDataStatistics);
        runtimeDataStatistics.setEditor(user.getUserName());
        runtimeDataStatistics.setUpdateTime(new Date());
        if (Objects.isNull(runtimeDataStatistics.getId())) {
            runtimeDataStatistics.setStatisticsDate(new Date());
            save(runtimeDataStatistics);
        } else {
            updateById(runtimeDataStatistics);
        }
    }

    public void review(Long id, User user) {
        RuntimeDataStatistics runtimeDataStatistics = runtimeDataStatisticsDao.selectById(id);
        if (Objects.isNull(runtimeDataStatistics)) {
            runtimeDataStatistics = new RuntimeDataStatistics();
            runtimeDataStatistics.setWaterGeneratedAmount(0.0);
            runtimeDataStatistics.setPumpingVolume(0.0);
            runtimeDataStatistics.setMaximumOutput(0.0);
            runtimeDataStatistics.setMinimumOutput(0.0);
            runtimeDataStatistics.setStartCount(0);
            runtimeDataStatistics.setUpperReservoirEffectiveStorageCapacity(0.0);
            runtimeDataStatistics.setUpperReservoirLowestWaterLevel(0.0);
            runtimeDataStatistics.setUpperReservoirStorageCapacityChange(0.0);
            runtimeDataStatistics.setUpperReservoirHighestWaterLevel(0.0);
            runtimeDataStatistics.setLowerReservoirEffectiveStorageCapacity(0.0);
            runtimeDataStatistics.setLowerReservoirLowestWaterLevel(0.0);
            runtimeDataStatistics.setLowerReservoirStorageCapacityChange(0.0);
            runtimeDataStatistics.setLowerReservoirHighestWaterLevel(0.0);
        }
        if (ReviewedEnum.REVIEWED.getState().equals(runtimeDataStatistics.getReviewed())) {
            throw new BusinessException("该运行数据已审核！");
        }
        runtimeDataStatistics.setReviewed(ReviewedEnum.REVIEWED.getState());
        runtimeDataStatistics.setReviewer(user.getUserName());
        runtimeDataStatistics.setUpdateTime(new Date());
        if (Objects.isNull(runtimeDataStatistics.getId())) {
            save(runtimeDataStatistics);
        } else {
            updateById(runtimeDataStatistics);
        }
    }

    public RuntimeDataStatisticsDetailDTO detail(Long id) {
        RuntimeDataStatistics runtimeDataStatistics = Optional.ofNullable(runtimeDataStatisticsDao.selectById(id))
                .orElseThrow(() -> new BusinessException("该运行数据不存在！"));
        RuntimeDataStatisticsDetailDTO runtimeDataStatisticsDetailDTO = new RuntimeDataStatisticsDetailDTO();
        BeanUtils.copyProperties(runtimeDataStatistics, runtimeDataStatisticsDetailDTO);
        runtimeDataStatisticsDetailDTO.setId(runtimeDataStatistics.getId());
        return runtimeDataStatisticsDetailDTO;
    }

    public List<RuntimeDataStatisticsDetailDTO> queryPage(String reviewer, String editor, String startDate, String endDate) {
        LambdaQueryChainWrapper<RuntimeDataStatistics> wrapper = new LambdaQueryChainWrapper<>(runtimeDataStatisticsDao);
        List<RuntimeDataStatistics> list = wrapper.eq(StringUtils.isNotBlank(reviewer), RuntimeDataStatistics::getReviewer, reviewer)
                .eq(StringUtils.isNotBlank(editor), RuntimeDataStatistics::getEditor, editor)
                .ge(StringUtils.isNotBlank(startDate), RuntimeDataStatistics::getStatisticsDate, startDate)
                .le(StringUtils.isNotBlank(endDate), RuntimeDataStatistics::getStatisticsDate, endDate)
                .list();
        return list.stream().map(i -> {
            RuntimeDataStatisticsDetailDTO runtimeDataStatisticsDetailDTO = new RuntimeDataStatisticsDetailDTO();
            BeanUtils.copyProperties(i, runtimeDataStatisticsDetailDTO);
            runtimeDataStatisticsDetailDTO.setId(i.getId());
            return runtimeDataStatisticsDetailDTO;
        }).collect(Collectors.toList());
    }

    /**
     * 定时任务生成运行数据
     * 每天0点上报前一天的数据：
     * 1、计划发电量、计划抽水电量，因CSG_FHQX0_FINAL测点不能取到正确的最终负荷数据，故手工输入；
     * 2、实际发电量、实际抽水（电）量，手工输入，保存后不允许修改，且无需审核。
     * 3、发电水量 = 发电工况时（上库库容变化量），上库库容测点：UPCAPACITY meteId:4829
     * 当次工况库容变化量 = 工况开始时间库容值 - 工况结束时间库容值，一天有多次
     * 4、抽水量 = 抽水工况时（上库库容变化量），下库库容测点：DOWNCAPACITY meteId:4830
     * 当次工况库容变化量 = 工况结束时间库容值 - 工况开始时间库容值，一天有多次
     * 5、最大出力：取QCZYG测点，统计前一天内，最大值 meteId:5814
     * 6、最小出力：取QCZYG测点，统计前一天内，最小值
     * 7、启停次数：统计前一天内所有机组的总启动次数
     * 8、上/下库最高水位：取上/下库库容测点，前一天内最大值
     * 9、上/下库最低水位：取上/下库库容测点，前一天内最小值
     * 10、上/下库库容变化量：取上/下库库容测点，当天0点值 - 前一天0点值
     * 11、上/下库24点有效库容：当天0点值 - 定值（上库：84.1，下库：131.4）
     */
    @Transactional(rollbackFor = Exception.class)
    public void runtimeDataStatistics() {
        LambdaQueryChainWrapper<RuntimeDataStatistics> wrapper = new LambdaQueryChainWrapper<>(runtimeDataStatisticsDao);
        List<RuntimeDataStatistics> list = wrapper
                .ge(RuntimeDataStatistics::getStatisticsDate, DateUtil.beginOfDay(DateUtil.offsetDay(DateTime.now(), -1)))
                .lt(RuntimeDataStatistics::getStatisticsDate, DateUtil.beginOfDay(new Date()))
                .list();
        if (CollectionUtils.isNotEmpty(list)) {
            runtimeDataStatisticsDao.deleteBatchIds(list.stream().map(RuntimeDataStatistics::getId).collect(Collectors.toList()));
        }
        RuntimeDataStatistics runtimeDataStatistics = new RuntimeDataStatistics();
        LambdaQueryChainWrapper<MorningPaperConsts> baseWrapper = new LambdaQueryChainWrapper<>(baseMeteConstsDao);
        List<MorningPaperConsts> morningPaperConstsList = baseWrapper.list();
        //统计发电量和抽水量 找出1CRK00_MODE_DZ_G 1CRK00_MODE_DZ_P value为1和0的时间，查找tdEngine中两个时间点的值，算出差值。
        //统计发电量
        statisticsPowerGeneration(runtimeDataStatistics, morningPaperConstsList);
        //统计抽水电量
        statisticsWaterPumpingPower(runtimeDataStatistics, morningPaperConstsList);
        //统计最大最小出力
        statisticsOutput(runtimeDataStatistics, morningPaperConstsList);
        //统计启停次数
        statisticsUpCount(runtimeDataStatistics, morningPaperConstsList);
        //统计水位变化
        statisticsWaterLevel(runtimeDataStatistics, morningPaperConstsList);
        //统计库容变化
        statisticsCapacityChange(runtimeDataStatistics, morningPaperConstsList);

        runtimeDataStatistics.setStatisticsDate(DateUtil.beginOfDay(DateUtil.offsetDay(DateTime.now(), -1)).toSqlDate());
        runtimeDataStatistics.setCreator(1L);
        runtimeDataStatistics.setUpdater(1L);
        runtimeDataStatisticsDao.insert(runtimeDataStatistics);
    }

    /**
     * 统计发电量
     *
     * @param runtimeDataStatistics
     */
    private void statisticsPowerGeneration(RuntimeDataStatistics runtimeDataStatistics, List<MorningPaperConsts> morningPaperConstsList) {
        double waterGenerateAmount = 0.0;
        DateTime yesterday = DateUtil.yesterday();
        DateTime start = DateUtil.beginOfDay(yesterday);
        DateTime end = DateUtil.beginOfDay(DateTime.now());
        List<MorningPaperConsts> list = morningPaperConstsList.stream().filter(i -> i.getMeteCode().contains(CRK00_MODE_DZ_G)).collect(Collectors.toList());
        Long meteId = morningPaperConstsList.stream().filter(i -> UP_CAPACITY.equals(i.getMeteCode())).findFirst().orElseThrow(() -> new BusinessException("未配置上水库测点基础表")).getMeteId();
        List<RealDataHis> u01GMeteHis;
        MorningPaperConsts u01 = list.stream().filter(i -> MachineAndWorkConditionConst.MACHINE_01_NO.equals(i.getMachineNo())).findFirst().orElse(new MorningPaperConsts());
        try {
            u01GMeteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YX + u01.getMeteId(), start, end);
        } catch (Exception e) {
            log.info("机组:{}对应测点不存在:{}", "U01", u01.getMeteId());
            u01GMeteHis = new ArrayList<>();
        }
        waterGenerateAmount += getWaterGenerateAmount(u01GMeteHis, meteId);
        log.info("U01发电量:{}", waterGenerateAmount);

        MorningPaperConsts u02 = list.stream().filter(i -> MachineAndWorkConditionConst.MACHINE_02_NO.equals(i.getMachineNo())).findFirst().orElse(new MorningPaperConsts());
        List<RealDataHis> u02GMeteHis;
        try {
            u02GMeteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YX + u02.getMeteId(), start, end);
        } catch (Exception e) {
            log.info("机组:{}对应测点不存在:{}", "U02", u02.getMeteId());
            u02GMeteHis = new ArrayList<>();
        }
        waterGenerateAmount += getWaterGenerateAmount(u02GMeteHis, meteId);
        log.info("U02发电量:{}", waterGenerateAmount);

        MorningPaperConsts u03 = list.stream().filter(i -> MachineAndWorkConditionConst.MACHINE_03_NO.equals(i.getMachineNo())).findFirst().orElse(new MorningPaperConsts());
        List<RealDataHis> u03GMeteHis;
        try {
            u03GMeteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YX + u03.getMeteId(), start, end);
        } catch (Exception e) {
            log.info("机组:{}对应测点不存在:{}", "U03", u03.getMeteId());
            u03GMeteHis = new ArrayList<>();
        }
        waterGenerateAmount += getWaterGenerateAmount(u03GMeteHis, meteId);
        log.info("U03发电量:{}", waterGenerateAmount);
        MorningPaperConsts u04 = list.stream().filter(i -> MachineAndWorkConditionConst.MACHINE_04_NO.equals(i.getMachineNo())).findFirst().orElse(new MorningPaperConsts());

        List<RealDataHis> u04GMeteHis;
        try {
            u04GMeteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YX + u04.getMeteId(), start, end);
        } catch (Exception e) {
            log.info("机组:{}对应测点不存在:{}", "U04", u04.getMeteId());
            u04GMeteHis = new ArrayList<>();
        }
        waterGenerateAmount += getWaterGenerateAmount(u04GMeteHis, meteId);
        log.info("U04发电量:{}", waterGenerateAmount);
        runtimeDataStatistics.setWaterGeneratedAmount(BigDecimal.valueOf(waterGenerateAmount).setScale(2, RoundingMode.HALF_UP).doubleValue());
    }

    /**
     * 统计抽水电量
     *
     * @param runtimeDataStatistics
     */
    private void statisticsWaterPumpingPower(RuntimeDataStatistics runtimeDataStatistics, List<MorningPaperConsts> morningPaperConstsList) {

        double pumpingVolume = 0.0;
        DateTime yesterday = DateUtil.yesterday();
        DateTime start = DateUtil.beginOfDay(yesterday);
        DateTime end = DateUtil.beginOfDay(DateTime.now());
        List<MorningPaperConsts> list = morningPaperConstsList.stream().filter(i -> i.getMeteCode().contains(CRK00_MODE_DZ_P)).collect(Collectors.toList());
        Long meteId = morningPaperConstsList.stream().filter(i -> DOWN_CAPACITY.equals(i.getMeteCode())).findFirst().orElseThrow(() -> new BusinessException("未配置下水库测点基础表")).getMeteId();
        MorningPaperConsts u01 = list.stream().filter(i -> MachineAndWorkConditionConst.MACHINE_01_NO.equals(i.getMachineNo())).findFirst().orElse(new MorningPaperConsts());
        List<RealDataHis> u01GMeteHis;
        try {
            u01GMeteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YX + u01.getMeteId(), start, end);
        } catch (Exception e) {
            log.info("机组:{}对应测点不存在:{}", "U01", u01.getMeteId());
            u01GMeteHis = new ArrayList<>();
        }
        pumpingVolume += getWaterGenerateAmount(u01GMeteHis, meteId);
        log.info("U01抽水电量:{}", pumpingVolume);

        MorningPaperConsts u02 = list.stream().filter(i -> MachineAndWorkConditionConst.MACHINE_02_NO.equals(i.getMachineNo())).findFirst().orElse(new MorningPaperConsts());
        List<RealDataHis> u02GMeteHis;
        try {
            u02GMeteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YX + u02.getMeteId(), start, end);
        } catch (Exception e) {
            log.info("机组:{}对应测点不存在:{}", "U02", u02.getMeteId());
            u02GMeteHis = new ArrayList<>();
        }
        pumpingVolume += getWaterGenerateAmount(u02GMeteHis, meteId);
        log.info("U02抽水电量:{}", pumpingVolume);

        MorningPaperConsts u03 = list.stream().filter(i -> MachineAndWorkConditionConst.MACHINE_03_NO.equals(i.getMachineNo())).findFirst().orElse(new MorningPaperConsts());
        List<RealDataHis> u03GMeteHis;
        try {
            u03GMeteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YX + u03.getMeteId(), start, end);
        } catch (Exception e) {
            log.info("机组:{}对应测点不存在:{}", "U03", u03.getMeteId());
            u03GMeteHis = new ArrayList<>();
        }
        pumpingVolume += getWaterGenerateAmount(u03GMeteHis, meteId);
        log.info("U03抽水电量:{}", pumpingVolume);

        MorningPaperConsts u04 = list.stream().filter(i -> MachineAndWorkConditionConst.MACHINE_04_NO.equals(i.getMachineNo())).findFirst().orElse(new MorningPaperConsts());

        List<RealDataHis> u04GMeteHis;
        try {
            u04GMeteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YX + u04.getMeteId(), start, end);
        } catch (Exception e) {
            log.info("机组:{}对应测点不存在:{}", "U04", u04.getMeteId());
            u04GMeteHis = new ArrayList<>();
        }
        pumpingVolume += getWaterGenerateAmount(u04GMeteHis, meteId);
        log.info("U04抽水电量:{}", pumpingVolume);
        runtimeDataStatistics.setPumpingVolume(BigDecimal.valueOf(pumpingVolume).setScale(2, RoundingMode.HALF_UP).doubleValue());
    }

    private double getWaterGenerateAmount(List<RealDataHis> u01GMeteHis, Long meteId) {
        double amount = 0.0;
        if (CollectionUtils.isEmpty(u01GMeteHis)) {
            return amount;
        }
        u01GMeteHis = u01GMeteHis.stream().sorted(Comparator.comparingLong(r -> r.getIdTime().getTime())).collect(Collectors.toList());
        int inx = -1;
        for (int i = 0; i < u01GMeteHis.size(); i++) {
            if (Consts.VALID_METE_VALUE.equals(u01GMeteHis.get(i).getValue())) {
                inx = i;
                break;
            }
        }
        if (inx != -1) {
            u01GMeteHis = u01GMeteHis.subList(inx, u01GMeteHis.size());
        }
        log.info("发电/抽水工况测点数据:{}", JSON.toJSONString(u01GMeteHis));
        List<RealDataHis> validMeteHis = u01GMeteHis.stream().filter(f -> Consts.VALID_METE_VALUE.equals(f.getValue())).collect(Collectors.toList());
        List<RealDataHis> invalidMeteHis = u01GMeteHis.stream().filter(f -> !Consts.VALID_METE_VALUE.equals(f.getValue())).collect(Collectors.toList());
        for (int i = 0; i < validMeteHis.size(); i++) {
            if (Consts.VALID_METE_VALUE.equals(validMeteHis.get(i).getValue())) {
                Timestamp startTime = validMeteHis.get(i).getIdTime();
                if (i < invalidMeteHis.size() && Objects.nonNull(invalidMeteHis.get(i))) {
                    RealDataHis realDataHis = invalidMeteHis.get(i);
                    Timestamp endTime = realDataHis.getIdTime();
                    //查找工况开始（value为1）的时间点(后10秒内)对应的UP或者DOWN的数据，如果有多个取最近的
                    List<RealDataHis> startRealDataHisList;
                    try {
                        startRealDataHisList = dataHisTdMapper.selectByTableNameTimeGeStartTime(TableDictConsts.DATA_HIS_PREFIX_YC + meteId, startTime);
                    } catch (Exception e) {
                        startRealDataHisList = new ArrayList<>();
                    }
                    log.info("测点{},工况开始库容数据:{}", meteId, JSON.toJSONString(startRealDataHisList));
                    RealDataHis startRealDataHis = compareTime(startRealDataHisList, startTime);
                    //查找工况结束（value为0）的时间点(后10秒内)对应的UP或者DOWN的数据，如果有多个取最近的
                    List<RealDataHis> endRealDataHisList;
                    try {
                        endRealDataHisList = dataHisTdMapper.selectByTableNameTimeGeStartTime(TableDictConsts.DATA_HIS_PREFIX_YC + meteId, endTime);
                    } catch (Exception e) {
                        endRealDataHisList = new ArrayList<>();
                    }
                    log.info("测点{},工况结束库容数据:{}", meteId, JSON.toJSONString(endRealDataHisList));
                    RealDataHis endRealDataHis = compareTime(endRealDataHisList, endTime);
                    if (Objects.nonNull(startRealDataHis) && Objects.nonNull(endRealDataHis)) {
                        //该次工况启动的库容差值
                        amount += Math.abs(new BigDecimal(startRealDataHis.getValue()).subtract(new BigDecimal(endRealDataHis.getValue())).doubleValue());
                    }
                } else {
                    log.info("工况存在有1没0的数据");
                    //如果不存在结束0的测点数据，需要将00:00:00.000作为结束时间，去查遥测测点的值
                    Timestamp endTime = DateUtil.beginOfDay(new Date()).toTimestamp();
                    //查找工况开始（value为1）的时间点(后10秒内)对应的UP或者DOWN的数据，如果有多个取最近的
                    List<RealDataHis> startRealDataHisList = dataHisTdMapper.selectByTableNameTimeGeStartTime(TableDictConsts.DATA_HIS_PREFIX_YC + meteId, startTime);
                    log.info("测点{},工况开始库容数据:{}", meteId, JSON.toJSONString(startRealDataHisList));
                    RealDataHis startRealDataHis = compareTime(startRealDataHisList, startTime);
                    //查找工况结束（value为0）的时间点(后10秒内)对应的UP或者DOWN的数据，如果有多个取最近的
                    RealDataHis endRealDataHis = dataHisTdMapper.selectByTableNameLeTimeDescLimit1(TableDictConsts.DATA_HIS_PREFIX_YC + meteId, startTime, endTime);
                    log.info("测点{},工况结束库容数据:{}", meteId, JSON.toJSONString(endRealDataHis));
                    if (Objects.nonNull(startRealDataHis) && Objects.nonNull(endRealDataHis)) {
                        //该次工况启动的库容差值
                        amount += Math.abs(new BigDecimal(startRealDataHis.getValue()).subtract(new BigDecimal(endRealDataHis.getValue())).doubleValue());
                    }
                }
            }
        }
        return amount;
    }

    private static RealDataHis compareTime(List<RealDataHis> realDataHis, Timestamp targetDateTime) {
        return realDataHis.stream()
                .min(Comparator.comparingLong(data -> Math.abs(data.getIdTime().getTime() - targetDateTime.getTime())))
                .orElse(null);
    }

    /**
     * 统计最大、最小出力
     *
     * @return
     */
    public void statisticsOutput(RuntimeDataStatistics runtimeDataStatistics, List<MorningPaperConsts> morningPaperConstsList) {
        MorningPaperConsts morningPaperConsts = morningPaperConstsList.stream().filter(i -> QCZYG.equals(i.getMeteCode())).findFirst().orElseThrow(() -> new BusinessException("未配置总功基础表测点"));
        Long meteId = morningPaperConsts.getMeteId();
        List<RealDataHis> meteHis;
        try {
            meteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YC + meteId, DateUtil.beginOfDay(DateUtil.yesterday()), DateUtil.beginOfDay(new Date()));
        } catch (Exception e) {
            meteHis = new ArrayList<>();
        }
        if (CollectionUtils.isNotEmpty(meteHis)) {
            RealDataHis maxRealDataHis = meteHis.stream().max(Comparator.comparingDouble(data -> Double.parseDouble(data.getValue()))).orElse(new RealDataHis());
            RealDataHis minRealDataHis = meteHis.stream().min(Comparator.comparingDouble(data -> Double.parseDouble(data.getValue()))).orElse(new RealDataHis());
            runtimeDataStatistics.setMaximumOutput(BigDecimal.valueOf(Double.parseDouble(maxRealDataHis.getValue())).setScale(2, RoundingMode.HALF_UP).doubleValue());
            runtimeDataStatistics.setMinimumOutput(BigDecimal.valueOf(Double.parseDouble(minRealDataHis.getValue())).setScale(2, RoundingMode.HALF_UP).doubleValue());
        }
    }

    /**
     * 8、上/下库最高水位：取上/下库库容测点，前一天内最大值
     * 9、上/下库最低水位：取上/下库库容测点，前一天内最小值
     *
     * @param runtimeDataStatistics
     */
    public void statisticsWaterLevel(RuntimeDataStatistics runtimeDataStatistics, List<MorningPaperConsts> morningPaperConstsList) {
        MorningPaperConsts upBaseMeteConsts = morningPaperConstsList.stream().filter(i -> UP_CAPACITY.equals(i.getMeteCode())).findFirst().orElseThrow(() -> new BusinessException("未配置上水库测点基础表"));
        MorningPaperConsts downBaseMeteConsts = morningPaperConstsList.stream().filter(i -> DOWN_CAPACITY.equals(i.getMeteCode())).findFirst().orElseThrow(() -> new BusinessException("未配置下水库测点基础表"));
        Long upMeteId = upBaseMeteConsts.getMeteId();
        Long lowMeteId = downBaseMeteConsts.getMeteId();
        List<RealDataHis> upMeteHis;
        try {
            upMeteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YC + upMeteId, DateUtil.beginOfDay(DateUtil.yesterday()), DateUtil.beginOfDay(new Date()));
        } catch (Exception e) {
            upMeteHis = new ArrayList<>();
        }
        List<RealDataHis> lowMeteHis;
        try {
            lowMeteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YC + lowMeteId, DateUtil.beginOfDay(DateUtil.yesterday()), DateUtil.beginOfDay(new Date()));
        } catch (Exception e) {
            lowMeteHis = new ArrayList<>();
        }
        if (CollectionUtils.isNotEmpty(upMeteHis)) {
            RealDataHis upMinRealDataHis = upMeteHis.stream().min(Comparator.comparingDouble(data -> Double.parseDouble(data.getValue()))).orElse(new RealDataHis());
            RealDataHis upMaxRealDataHis = upMeteHis.stream().max(Comparator.comparingDouble(data -> Double.parseDouble(data.getValue()))).orElse(new RealDataHis());
            runtimeDataStatistics.setUpperReservoirHighestWaterLevel(Double.parseDouble(upMaxRealDataHis.getValue()));
            runtimeDataStatistics.setUpperReservoirLowestWaterLevel(Double.parseDouble(upMinRealDataHis.getValue()));
        }
        if (CollectionUtils.isNotEmpty(lowMeteHis)) {
            RealDataHis lowMinRealDataHis = lowMeteHis.stream().min(Comparator.comparingDouble(data -> Double.parseDouble(data.getValue()))).orElse(new RealDataHis());
            RealDataHis lowMaxRealDataHis = lowMeteHis.stream().max(Comparator.comparingDouble(data -> Double.parseDouble(data.getValue()))).orElse(new RealDataHis());
            runtimeDataStatistics.setLowerReservoirHighestWaterLevel(Double.parseDouble(lowMaxRealDataHis.getValue()));
            runtimeDataStatistics.setLowerReservoirLowestWaterLevel(Double.parseDouble(lowMinRealDataHis.getValue()));
        }
    }

    /**
     * 10、上/下库库容变化量：取上/下库库容测点，当天00:00:01.000点以前的第一条值 - 前一天00:00:00.000后的第一条点值
     * 11、上/下库24点有效库容：当天0点值 - 定值（上库：84.1，下库：131.4）
     */
    public void statisticsCapacityChange(RuntimeDataStatistics runtimeDataStatistics, List<MorningPaperConsts> morningPaperConstsList) {
        MorningPaperConsts upBaseMeteConsts = morningPaperConstsList.stream().filter(i -> UP_CAPACITY.equals(i.getMeteCode())).findFirst().orElseThrow(() -> new BusinessException("未配置上水库测点基础表"));
        MorningPaperConsts downBaseMeteConsts = morningPaperConstsList.stream().filter(i -> DOWN_CAPACITY.equals(i.getMeteCode())).findFirst().orElseThrow(() -> new BusinessException("未配置下水库测点基础表"));
        Long upMeteId = upBaseMeteConsts.getMeteId();
        Long lowMeteId = downBaseMeteConsts.getMeteId();
//        //取今天00：00：00：000之前的第一条
//        DateTime thisDayEndTime = DateUtil.beginOfDay(new Date());
//        thisDayEndTime.setField(DateField.SECOND, 1);
//        //取昨天00：00：00：000之后的第一条
//        DateTime lastDayEndTime = DateUtil.beginOfDay(DateUtil.yesterday());
//        lastDayEndTime.setField(DateField.SECOND, 1);
        //取昨天00：00：00：000到今天00：00：00：000之间的最后一条-第一条
        DateTime thisDayEndTime = DateUtil.beginOfDay(new Date());
        DateTime lastDayEndTime = DateUtil.beginOfDay(DateUtil.yesterday());
        RealDataHis thisDayUpMeteHis = null;
        RealDataHis lastDayUpMeteHis = null;
        try {
            thisDayUpMeteHis = dataHisTdMapper.selectByTableNameLeTimeDescLimit1(TableDictConsts.DATA_HIS_PREFIX_YC + upMeteId, lastDayEndTime, thisDayEndTime);
            lastDayUpMeteHis = dataHisTdMapper.selectByTableNameGeTimeDescLimit1(TableDictConsts.DATA_HIS_PREFIX_YC + upMeteId, lastDayEndTime);
        } catch (Exception e) {
            log.info("TdEngine该表不存在:{}", TableDictConsts.DATA_HIS_PREFIX_YC + upMeteId);
        }
        RealDataHis thisDayLowMeteHis = null;
        RealDataHis lastDayLowMeteHis = null;
        try {
            thisDayLowMeteHis = dataHisTdMapper.selectByTableNameLeTimeDescLimit1(TableDictConsts.DATA_HIS_PREFIX_YC + lowMeteId, lastDayEndTime, thisDayEndTime);
            lastDayLowMeteHis = dataHisTdMapper.selectByTableNameGeTimeDescLimit1(TableDictConsts.DATA_HIS_PREFIX_YC + lowMeteId, lastDayEndTime);
        } catch (Exception e) {
            log.info("TdEngine该表不存在:{}", TableDictConsts.DATA_HIS_PREFIX_YC + lowMeteId);
        }

        String thisDayUpValue = Objects.nonNull(thisDayUpMeteHis) ? thisDayUpMeteHis.getValue() : "0.0";
        String lastDayUpValue = Objects.nonNull(lastDayUpMeteHis) ? lastDayUpMeteHis.getValue() : "0.0";
        String thisDayLowValue = Objects.nonNull(thisDayLowMeteHis) ? thisDayLowMeteHis.getValue() : "0.0";
        String lastDayLowValue = Objects.nonNull(lastDayLowMeteHis) ? lastDayLowMeteHis.getValue() : "0.0";
        runtimeDataStatistics.setUpperReservoirStorageCapacityChange(Math.abs(new BigDecimal(thisDayUpValue).subtract(new BigDecimal(lastDayUpValue)).doubleValue()));
        runtimeDataStatistics.setLowerReservoirStorageCapacityChange(Math.abs(new BigDecimal(thisDayLowValue).subtract(new BigDecimal(lastDayLowValue)).doubleValue()));
        runtimeDataStatistics.setUpperReservoirEffectiveStorageCapacity(Objects.nonNull(thisDayUpMeteHis) ? Math.abs(new BigDecimal(thisDayUpValue).subtract(BigDecimal.valueOf(84.1)).doubleValue()) : null);
        runtimeDataStatistics.setLowerReservoirEffectiveStorageCapacity(Objects.nonNull(thisDayLowMeteHis) ? Math.abs(new BigDecimal(thisDayLowValue).subtract(BigDecimal.valueOf(131.4)).doubleValue()) : null);
    }


    /**
     * 统计启停次数
     *
     * @return
     */
    private void statisticsUpCount(RuntimeDataStatistics runtimeDataStatistics, List<MorningPaperConsts> morningPaperConstsList) {
        //获取四个机组的pc p g工况测点id，然后统计出现1的次数
        List<MorningPaperConsts> list = morningPaperConstsList.stream().filter(i -> 0 == i.getType()).collect(Collectors.toList());
        //meteId集合
        List<Long> meteIds = list.stream().map(MorningPaperConsts::getMeteId).collect(Collectors.toList());
        AtomicReference<Integer> count = new AtomicReference<>(0);
        meteIds.forEach(meteId -> {
            List<RealDataHis> meteHis;
            try {
                meteHis = dataHisTdMapper.selectByTableNameTimeNoEndTime(TableDictConsts.DATA_HIS_PREFIX_YX + meteId, DateUtil.beginOfDay(DateUtil.yesterday()), DateUtil.beginOfDay(new Date()));
            } catch (Exception e) {
                meteHis = new ArrayList<>();
            }
            log.info("该测点对应td数据:{}", JSON.toJSONString(meteHis));
            if (CollectionUtils.isNotEmpty(meteHis)) {
                BaseMete baseMete = getBaseMeteYxCache(meteId);
                Integer validMeteValue = baseMete.getValidMeteValue();
                List<RealDataHis> finalMeteHis = meteHis;
                count.updateAndGet(v -> Math.toIntExact(v + finalMeteHis.stream().filter(mete -> validMeteValue.equals(Integer.valueOf(mete.getValue()))).count()));
            }
        });
        log.info("启停次数:{}", count.get());
        runtimeDataStatistics.setStartCount(count.get());
    }

    protected BaseMete getBaseMeteYxCache(Long meteId) {
        return redisUtil.getBean(RedisKeyEnum.BASE_METE_ENTITY_YX.key(meteId), BaseMete.class);
    }

    public RuntimeDataStatisticsDetailDTO get() {
        DateTime beginOfDay = DateUtil.beginOfDay(new Date());
        DateTime endOfDay = DateUtil.endOfDay(new Date());
        LambdaQueryChainWrapper<RuntimeDataStatistics> wrapper = new LambdaQueryChainWrapper<>(runtimeDataStatisticsDao);
        List<RuntimeDataStatistics> list = wrapper
                .le(RuntimeDataStatistics::getStatisticsDate, endOfDay)
                .orderByDesc(RuntimeDataStatistics::getStatisticsDate)
                .list();
        RuntimeDataStatisticsDetailDTO runtimeDataStatisticsDetailDTO;
        if (CollectionUtils.isEmpty(list)) {
            runtimeDataStatisticsDetailDTO = new RuntimeDataStatisticsDetailDTO();
            runtimeDataStatisticsDetailDTO.setWaterGeneratedAmount(0.0);
            runtimeDataStatisticsDetailDTO.setPumpingVolume(0.0);
            runtimeDataStatisticsDetailDTO.setMaximumOutput(0.0);
            runtimeDataStatisticsDetailDTO.setMinimumOutput(0.0);
            runtimeDataStatisticsDetailDTO.setStartCount(0);
            runtimeDataStatisticsDetailDTO.setUpperReservoirEffectiveStorageCapacity(0.0);
            runtimeDataStatisticsDetailDTO.setUpperReservoirLowestWaterLevel(0.0);
            runtimeDataStatisticsDetailDTO.setUpperReservoirStorageCapacityChange(0.0);
            runtimeDataStatisticsDetailDTO.setUpperReservoirHighestWaterLevel(0.0);
            runtimeDataStatisticsDetailDTO.setLowerReservoirEffectiveStorageCapacity(0.0);
            runtimeDataStatisticsDetailDTO.setLowerReservoirLowestWaterLevel(0.0);
            runtimeDataStatisticsDetailDTO.setLowerReservoirStorageCapacityChange(0.0);
            runtimeDataStatisticsDetailDTO.setLowerReservoirHighestWaterLevel(0.0);
            runtimeDataStatisticsDetailDTO.setStatisticsDate(DateUtil.yesterday());
        } else {
            RuntimeDataStatistics runtimeDataStatistics = list.get(0);
            runtimeDataStatisticsDetailDTO = new RuntimeDataStatisticsDetailDTO();
            BeanUtils.copyProperties(runtimeDataStatistics, runtimeDataStatisticsDetailDTO);
            runtimeDataStatisticsDetailDTO.setId(runtimeDataStatistics.getId());
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
//        String formattedDate = sdf.format(DateTime.now().offset(DateField.HOUR, -24));
        String formattedDate = sdf.format(runtimeDataStatisticsDetailDTO.getStatisticsDate());
        runtimeDataStatisticsDetailDTO.setTitle(String.format(TITLE, formattedDate));
        return runtimeDataStatisticsDetailDTO;
    }

    public StartCountStatisticsDTO startCountStatistics(String startDate, String endDate) {
        if (DateUtil.compare(new Date(), DateUtil.parseDate(startDate)) < 0) {
            throw new BusinessException("查询起始时间不能大于当前时间");
        }
        //统计g p pc 工况启动次数及时长
        LambdaQueryChainWrapper<WorkFlowInstanceResult> wrapper = new LambdaQueryChainWrapper<>(workFlowInstanceResultDao);
        // 今天之前的统计结果
        List<WorkFlowInstanceResult> instanceResultList = wrapper
                .ge(StringUtils.isNotBlank(startDate), WorkFlowInstanceResult::getStatisticalDate, startDate)
                .lt(StringUtils.isNotBlank(endDate), WorkFlowInstanceResult::getStatisticalDate, endDate)
                .list();
        List<WorkFlowInstanceResult> u01GWorkFlowInstanceResult;
        List<WorkFlowInstanceResult> u01PWorkFlowInstanceResult;
        List<WorkFlowInstanceResult> u01PCWorkFlowInstanceResult;
        List<WorkFlowInstanceResult> u02GWorkFlowInstanceResult;
        List<WorkFlowInstanceResult> u02PWorkFlowInstanceResult;
        List<WorkFlowInstanceResult> u02PCWorkFlowInstanceResult;
        List<WorkFlowInstanceResult> u03GWorkFlowInstanceResult;
        List<WorkFlowInstanceResult> u03PWorkFlowInstanceResult;
        List<WorkFlowInstanceResult> u03PCWorkFlowInstanceResult;
        List<WorkFlowInstanceResult> u04GWorkFlowInstanceResult;
        List<WorkFlowInstanceResult> u04PWorkFlowInstanceResult;
        List<WorkFlowInstanceResult> u04PCWorkFlowInstanceResult;
        // 机组编号:
        if (CollectionUtils.isNotEmpty(instanceResultList)) {
            Map<String, List<WorkFlowInstanceResult>> machineNoResultMap = instanceResultList.stream().collect(Collectors.groupingBy(WorkFlowInstanceResult::getMachineNo));
            List<WorkFlowInstanceResult> u01WorkFlowInstanceResults = machineNoResultMap.get(MachineAndWorkConditionConst.MACHINE_01_NO);
            List<WorkFlowInstanceResult> u02WorkFlowInstanceResults = machineNoResultMap.get(MachineAndWorkConditionConst.MACHINE_02_NO);
            List<WorkFlowInstanceResult> u03WorkFlowInstanceResults = machineNoResultMap.get(MachineAndWorkConditionConst.MACHINE_03_NO);
            List<WorkFlowInstanceResult> u04WorkFlowInstanceResults = machineNoResultMap.get(MachineAndWorkConditionConst.MACHINE_04_NO);

            //机组 g p pc工况统计结果
            u01GWorkFlowInstanceResult = u01WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_G.equals(i.getWcName())).collect(Collectors.toList());
            u01PWorkFlowInstanceResult = u01WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_P.equals(i.getWcName())).collect(Collectors.toList());
            u01PCWorkFlowInstanceResult = u01WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_PC.equals(i.getWcName())).collect(Collectors.toList());

            u02GWorkFlowInstanceResult = u02WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_G.equals(i.getWcName())).collect(Collectors.toList());
            u02PWorkFlowInstanceResult = u02WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_P.equals(i.getWcName())).collect(Collectors.toList());
            u02PCWorkFlowInstanceResult = u02WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_PC.equals(i.getWcName())).collect(Collectors.toList());

            u03GWorkFlowInstanceResult = u03WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_G.equals(i.getWcName())).collect(Collectors.toList());
            u03PWorkFlowInstanceResult = u03WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_P.equals(i.getWcName())).collect(Collectors.toList());
            u03PCWorkFlowInstanceResult = u03WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_PC.equals(i.getWcName())).collect(Collectors.toList());

            u04GWorkFlowInstanceResult = u04WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_G.equals(i.getWcName())).collect(Collectors.toList());
            u04PWorkFlowInstanceResult = u04WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_P.equals(i.getWcName())).collect(Collectors.toList());
            u04PCWorkFlowInstanceResult = u04WorkFlowInstanceResults.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_PC.equals(i.getWcName())).collect(Collectors.toList());
        } else {
            u01GWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U01_G).successNum(0).ranHours(0.0).build());
            u01PWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U01_P).successNum(0).ranHours(0.0).build());
            u01PCWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U01_PC).successNum(0).ranHours(0.0).build());

            u02GWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U02_G).successNum(0).ranHours(0.0).build());
            u02PWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U02_P).successNum(0).ranHours(0.0).build());
            u02PCWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U02_PC).successNum(0).ranHours(0.0).build());

            u03GWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U03_G).successNum(0).ranHours(0.0).build());
            u03PWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U03_P).successNum(0).ranHours(0.0).build());
            u03PCWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U03_PC).successNum(0).ranHours(0.0).build());

            u04GWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U04_G).successNum(0).ranHours(0.0).build());
            u04PWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U04_P).successNum(0).ranHours(0.0).build());
            u04PCWorkFlowInstanceResult = Lists.newArrayList(WorkFlowInstanceResult.builder().meteCode(MachineAndWorkConditionConst.U04_PC).successNum(0).ranHours(0.0).build());
        }
        //获取四个机组的pc p g工况测点id，然后统计出现1的次数
        LambdaQueryChainWrapper<MorningPaperConsts> baseMeteConstsLambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(baseMeteConstsDao);
        List<MorningPaperConsts> list = baseMeteConstsLambdaQueryChainWrapper.eq(MorningPaperConsts::getType, 0).list();
        Map<String, Integer> countMap = new HashMap<>();
        Map<String, Double> costMap = new HashMap<>();
        if (!(DateUtil.compare(DateUtil.beginOfDay(new Date()), DateUtil.parseDate(endDate)) >= 0)) {
            //如果结束时间在今天之前，不应该查询今天数据
            list.forEach(mete -> {
                List<RealDataHis> meteHis = new ArrayList<>();
                try {
                    meteHis = dataHisTdMapper.selectByTableNameTimeAsc(TableDictConsts.DATA_HIS_PREFIX_YX + mete.getMeteId(), DateUtil.beginOfDay(new Date()), DateUtil.date());
                    log.info("该测点对应td数据:{}", JSON.toJSONString(meteHis));
                    int count = 0;
                    double v = 0.0;
                    if (CollectionUtils.isNotEmpty(meteHis)) {
                        BaseMete baseMete = getBaseMeteYxCache(mete.getMeteId());
                        Integer validMeteValue = baseMete.getValidMeteValue();
                        count = Math.toIntExact(meteHis.stream().filter(i -> validMeteValue.equals(Integer.valueOf(i.getValue()))).count());
                        v = fillConditionHours(meteHis, baseMete);
                    }
                    countMap.put(mete.getMeteCode(), count);
                    costMap.put(mete.getMeteCode(), v);
                } catch (Exception e) {
                    log.info("该测点对应表不存在:{},message:{}", mete.getMeteId(), e.getMessage());
                }
            });
        }
        StartCountStatisticsDTO startCountStatisticsDTO = new StartCountStatisticsDTO();
        startCountStatisticsDTO.setMachineU01GStartCount(u01GWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u01GWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u01GWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU01PStartCount(u01PWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u01PWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u01PWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU01PCStartCount(u01PCWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u01PCWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u01PCWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU02GStartCount(u02GWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u02GWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u02GWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU02PStartCount(u02PWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u02PWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u02PWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU02PCStartCount(u02PCWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u02PCWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u02PCWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU03GStartCount(u03GWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u03GWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u03GWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU03PStartCount(u03PWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u03PWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u03PWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU03PCStartCount(u03PCWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u03PCWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u03PCWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU04GStartCount(u04GWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u04GWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u04GWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU04PStartCount(u04PWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u04PWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u04PWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU04PCStartCount(u04PCWorkFlowInstanceResult.stream().mapToInt(WorkFlowInstanceResult::getSuccessNum).sum() + (Objects.isNull(countMap.get(u04PCWorkFlowInstanceResult.get(0).getMeteCode())) ? 0 : countMap.get(u04PCWorkFlowInstanceResult.get(0).getMeteCode())));
        startCountStatisticsDTO.setMachineU01GRuntime(Math.abs(u01GWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u01GWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u01GWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setMachineU01PRuntime(Math.abs(u01PWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u01PWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u01PWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setMachineU01PCRuntime(Math.abs(u01PCWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u01PCWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u01PCWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setMachineU02GRuntime(Math.abs(u02GWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u02GWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u02GWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setMachineU02PRuntime(Math.abs(u02PWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u02PWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u02PWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setMachineU02PCRuntime(Math.abs(u02PCWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u02PCWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u02PCWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setMachineU03GRuntime(Math.abs(u03GWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u03GWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u03GWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setMachineU03PRuntime(Math.abs(u03PWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u03PWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u03PWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setMachineU03PCRuntime(Math.abs(u03PCWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u03PCWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u03PCWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setMachineU04GRuntime(Math.abs(u04GWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u04GWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u04GWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setMachineU04PRuntime(Math.abs(u04PWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u04PWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u04PWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setMachineU04PCRuntime(Math.abs(u04PCWorkFlowInstanceResult.stream().map(i -> BigDecimal.valueOf(i.getRanHours())).reduce(BigDecimal.ZERO, BigDecimal::add).add(BigDecimal.valueOf(Objects.isNull(costMap.get(u04PCWorkFlowInstanceResult.get(0).getMeteCode())) ? 0.0 : costMap.get(u04PCWorkFlowInstanceResult.get(0).getMeteCode()))).doubleValue()));
        startCountStatisticsDTO.setTotalGCount(startCountStatisticsDTO.getMachineU01GStartCount() + startCountStatisticsDTO.getMachineU02GStartCount() + startCountStatisticsDTO.getMachineU03GStartCount() + startCountStatisticsDTO.getMachineU04GStartCount());
        startCountStatisticsDTO.setTotalPCount(startCountStatisticsDTO.getMachineU01PStartCount() + startCountStatisticsDTO.getMachineU02PStartCount() + startCountStatisticsDTO.getMachineU03PStartCount() + startCountStatisticsDTO.getMachineU04PStartCount());
        startCountStatisticsDTO.setTotalPCCount(startCountStatisticsDTO.getMachineU01PCStartCount() + startCountStatisticsDTO.getMachineU02PCStartCount() + startCountStatisticsDTO.getMachineU03PCStartCount() + startCountStatisticsDTO.getMachineU04PCStartCount());
        startCountStatisticsDTO.setTotalCount(startCountStatisticsDTO.getTotalGCount() + startCountStatisticsDTO.getTotalPCount() + startCountStatisticsDTO.getTotalPCCount());
        startCountStatisticsDTO.setTotalGRuntime(Math.abs(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU01GRuntime()).add(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU02GRuntime())).add(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU03GRuntime())).add(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU04GRuntime())).doubleValue()));
        startCountStatisticsDTO.setTotalPRuntime(Math.abs(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU01PRuntime()).add(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU02PRuntime())).add(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU03PRuntime())).add(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU04PRuntime())).doubleValue()));
        startCountStatisticsDTO.setTotalPCRuntime(Math.abs(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU01PCRuntime()).add(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU02PCRuntime())).add(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU03PCRuntime())).add(BigDecimal.valueOf(startCountStatisticsDTO.getMachineU04PCRuntime())).doubleValue()));
        //查找失败次数
        List<FailureRecord> failureRecords = failureRecordService.list(startDate, endDate);
        int gFailCount = Math.toIntExact(failureRecords.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_G.equals(i.getFailureFw())).count());
        int pFailCount = Math.toIntExact(failureRecords.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_P.equals(i.getFailureFw())).count());
        int pcFailCount = Math.toIntExact(failureRecords.stream().filter(i -> MachineAndWorkConditionConst.CONDITION_PC.equals(i.getFailureFw())).count());
        double gPercent = BigDecimal.valueOf(gFailCount + startCountStatisticsDTO.getTotalGCount() == 0 ? BigDecimal.ZERO.doubleValue() : new BigDecimal(startCountStatisticsDTO.getTotalGCount()).divide(new BigDecimal(gFailCount + startCountStatisticsDTO.getTotalGCount()), 2, RoundingMode.HALF_UP).doubleValue()).multiply(new BigDecimal(100)).doubleValue();
        double pPercent = BigDecimal.valueOf(pFailCount + startCountStatisticsDTO.getTotalPCount() == 0 ? BigDecimal.ZERO.doubleValue() : new BigDecimal(startCountStatisticsDTO.getTotalPCount()).divide(new BigDecimal(pFailCount + startCountStatisticsDTO.getTotalPCount()), 2, RoundingMode.HALF_UP).doubleValue()).multiply(new BigDecimal(100)).doubleValue();
        double pcPercent = BigDecimal.valueOf(pcFailCount + startCountStatisticsDTO.getTotalPCCount() == 0 ? BigDecimal.ZERO.doubleValue() : new BigDecimal(startCountStatisticsDTO.getTotalPCCount()).divide(new BigDecimal(pcFailCount + startCountStatisticsDTO.getTotalPCCount()), 2, RoundingMode.HALF_UP).doubleValue()).multiply(new BigDecimal(100)).doubleValue();
        startCountStatisticsDTO.setGSuccessPercent(gPercent);
        startCountStatisticsDTO.setPSuccessPercent(pPercent);
        startCountStatisticsDTO.setPcSuccessPercent(pcPercent);
        return startCountStatisticsDTO;
    }

    protected Double fillConditionHours(List<RealDataHis> meteHis, BaseMete baseMete) {
        Integer validMeteValue = baseMete.getValidMeteValue();
        long sumTime = 0L;
        List<RealDataHis> validMeteHis = meteHis.stream().filter(f -> validMeteValue.toString().equals(f.getValue())).collect(Collectors.toList());
        List<RealDataHis> invalidMeteHis = meteHis.stream().filter(f -> !validMeteValue.toString().equals(f.getValue())).collect(Collectors.toList());
        for (int i = 0; i < validMeteHis.size(); i++) {
            Instant startTimeFlag = validMeteHis.get(i).getIdTime().toInstant();
            if (validMeteValue.equals(Integer.valueOf(validMeteHis.get(i).getValue()))) {
                if (i < invalidMeteHis.size() && Objects.nonNull(invalidMeteHis.get(i))) {
                    Instant endTimeFlag = invalidMeteHis.get(i).getIdTime().toInstant();
                    long millis = Duration.between(startTimeFlag, endTimeFlag).toMillis();
                    sumTime += millis;
                }
            } else {
                DateTime now = DateTime.now();
                now.setField(DateField.HOUR_OF_DAY, 0);
                now.setField(DateField.MINUTE, 0);
                now.setField(DateField.SECOND, 0);
                now.setField(DateField.MILLISECOND, 0);
                long millis = Duration.between(startTimeFlag, now.toInstant()).toMillis();
                sumTime += millis;
            }
        }
        BigDecimal hoursBigDecimal = new BigDecimal(sumTime).divide(DIVIDE_FACTOR, 2, RoundingMode.HALF_UP);
        return hoursBigDecimal.doubleValue();
    }
}
